package settingsapp.ui;

import java.io.BufferedReader;
import java.io.DataInputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Arrays;

import settingsapp.gestures.GestureHandler;
import settingsapp.gestures.GestureLibrary;
import settingsapp.settings.ClickableSettingChanger;
import settingsapp.settings.continuous.ContinuousSettingChanger;
import settingsapp.settings.continuous.ContinuousSettingFactory;
import settingsapp.settings.toggle.ToggleSettingFactory;
import settingsapp.ui.panel.Panel;
import settingsapp.ui.panel.TogglePanel;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.gesture.GestureOverlayView;
import android.os.Bundle;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.GestureDetector;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.widget.LinearLayout;
import android.widget.Toast;
import android.widget.LinearLayout.LayoutParams;
import ece.settingsapp.R;

public class PanelGridActivity extends Activity
{
	public static final String TAG = PanelGridActivity.class.getSimpleName();
	private static final String DEFAULT_LAYOUT_FILE = "default.txt";
	
	private ContinuousSettingChanger changerCallback;

	LinearLayout panelRootLayout; // parent layout
	View selectedView; // temporary storage for context menu (since it doesn't
						// pass the view it was initiated from)
	
	private boolean configurationMode = true;
	private boolean userIsGesturing = false;
	private boolean finishAfterResult = false;

	private GestureHandler gestureHandler;
	private GestureDetector gDetector;
	private View.OnTouchListener gListener;
	private GestureLibrary gLibrary;
		

	

	/**
	 * since we want the Panels to fill all available space, when adding buttons
	 * we set layout_weight to 1 and height/width to FILL_PARENT
	 */
	private static final LayoutParams PANEL_PARAMS = new LinearLayout.LayoutParams(
			LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT, 1f);
	
	/**
	 * Finds buttons parent LinearLayout and adds new button there
	 */
	private void addColumn(View siblingButton)
	{
		ViewParent parent = siblingButton.getParent();
		Log.d(TAG, "button parent is: " + parent.getClass());
		if (parent instanceof LinearLayout)
			addPanel(((LinearLayout) parent));
	}
	
	private LinearLayout addEmptyRow()
	{
		LinearLayout row = new LinearLayout(this);

		panelRootLayout.addView(row, PANEL_PARAMS);
		return row;
	}
	
	/**
	 * clicking a panel splits that row up into more buttons when in
	 * configuration mode, or does default action otherwise
	 */
	private void addPanel(LinearLayout row)
	{
		final Panel buttonView = new TogglePanel(this);

		// on button click, we add an additional button to the row

		buttonView.setOnClickListener(new OnClickListener()
		{
			public void onClick(View v)
			{
				if (configurationMode)
				{
					Log.d(TAG, "config mode onClick");
					PanelGridActivity.this.addColumn(v);
				} else
				{
					Log.d(TAG, "normal mode onClick");
					buttonView.getClickAction().run();
				}
			}
		});

		registerForContextMenu(buttonView);
		row.addView(buttonView, PANEL_PARAMS);
	}

	private void addPanel(LinearLayout row, final Panel buttonView)
	{
		// on button click, we add an additional button to the row

		buttonView.setOnClickListener(new OnClickListener()
		{
			public void onClick(View v)
			{
				if (configurationMode)
				{
					Log.d(TAG, "config mode onClick");
					PanelGridActivity.this.addColumn(v);
				} else
				{
					Log.d(TAG, "normal mode onClick");
					buttonView.getClickAction().run();
				}
			}
		});

		registerForContextMenu(buttonView);
		row.addView(buttonView, PANEL_PARAMS);
	}
		
	/**
	 * Adds a new row (LinearLayout), and fills that row with a new Panel
	 */
	private LinearLayout addRow()
	{
		LinearLayout row = new LinearLayout(this);

		addPanel(row);
		panelRootLayout.addView(row, PANEL_PARAMS);
		return row;
	}

	@Override
	public boolean dispatchTouchEvent(MotionEvent ev)
	{
		GestureOverlayView gov = (GestureOverlayView) findViewById(R.id.gestures);

		// Test if this event originated within one of the targets
		ev.offsetLocation(0, -50); // Account for the Title bar offset and
									// manual dispatch
		boolean targetTouched = (gestureHandler.detectTargetTouch(ev) != -1);

		if (userIsGesturing && ev.getAction() == MotionEvent.ACTION_UP)
		{
			userIsGesturing = false;
			gov.dispatchTouchEvent(ev);
			return true;
		} else if (userIsGesturing || targetTouched)
		{
			gov.dispatchTouchEvent(ev);
			userIsGesturing = true;
			return true;
		} else
		{
			ev.offsetLocation(0, 50); // Return the motionEvent to its normal
										// values for automatic dispatch
			findViewById(R.id.panel_layout).dispatchTouchEvent(ev);
			return true;
		}
	}

	
	public void gestureActionPerformed(int actionType, int actionId, boolean closingGesture)
	{
		ClickableSettingChanger changer;
		switch (actionType)
		{
		case 0:
			changer = ToggleSettingFactory.createSettingsObject(this, actionId);
			break;
		case 1:
			changer = ContinuousSettingFactory.createSettingsObject(this, actionId);
			break;
		default:
			return;
		}
		
		changer.clickAction();
		
		if(closingGesture)
		{
			if(actionType == 0)
				this.finish();
			else
				finishAfterResult = true;				
		}
	}
	
	private void initializeGesturing()
	{
		gLibrary = new GestureLibrary(this);
		gestureHandler = new GestureHandler(this, gLibrary);

		GestureOverlayView gov = (GestureOverlayView) findViewById(R.id.gestures);

		// Setup listeners
		gDetector = new GestureDetector(getApplicationContext(), gestureHandler);
		gListener = new View.OnTouchListener()
		{
			public boolean onTouch(View v, MotionEvent event)
			{
				if (gDetector.onTouchEvent(event))
				{
					return true;
				}

				// Check for the ACTION_UP event to know when an action is
				// finished
				if (event.getAction() == MotionEvent.ACTION_UP)
				{
					gestureHandler.onUp(event);
				}

				return false;
			}
		};

		gov.setOnTouchListener(gListener);
	}
	
	/**
	 * Prompts user to confirm deletion of entire layout, deletes if yes clicked
	 */
	private void removeAllViews()
	{
		DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener()
		{
			@Override
			public void onClick(DialogInterface dialog, int which)
			{
				switch (which)
				{
				case DialogInterface.BUTTON_POSITIVE:
					panelRootLayout.removeAllViews();
					break;

				case DialogInterface.BUTTON_NEGATIVE:
					// Do nothing
					break;
				}
			}
		};

		AlertDialog.Builder builder = new AlertDialog.Builder(this);
		builder.setMessage("Are you sure you want to erase your layout?")
				.setPositiveButton("Yes", dialogClickListener)
				.setNegativeButton("Cancel", dialogClickListener).show();
	}
	
	/**
	 * Removes Panel from row Removes the parent row if no panels left after
	 * Panel's removal
	 * 
	 * @param panel
	 */
	private void removePanel(View panel)
	{
		ViewParent parent = panel.getParent();

		if (parent instanceof ViewGroup)
		{
			ViewGroup ll = ((ViewGroup) parent);
			ll.removeView(panel);
			if (ll.getChildCount() < 1) // if we just removed the last button
										// from the view
			{
				panelRootLayout.removeView(ll); // remove this row
			}
		} else
			throw new RuntimeException(
					"Unexpected Layout type (expected LinearLayout)"); // this should never happen
	}

	
	/**
	 * Simply deletes the view row directly
	 */
	private void removeRow(View row)
	{
		ViewParent parent = row.getParent();

		if (parent instanceof ViewGroup)
		{
			ViewGroup ll = ((ViewGroup) parent);
			panelRootLayout.removeView(ll); // remove this row
		} else
			throw new RuntimeException(
					"Unexpected Layout type (expected LinearLayout)"); // this should never happen
	}
	

	/**
	 * Used to store a reference to the object which controls changing
	 * continuous settings, which is then used to change a preference through
	 * onActivityResult
	 */
	public void setChangerCallback(ContinuousSettingChanger changer)
	{
		changerCallback = changer;
	}
		
	@Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data)
    {
            super.onActivityResult(requestCode, resultCode, data);
            if (requestCode == ContinuousSettingChanger.CONTINUOUS_SETTING_REQUEST_CODE
                            && resultCode == RESULT_OK)
                    changerCallback.handleSettingChange(data.getIntExtra(
                                    ContinuousSettingChanger.ONSET_RESULT_VALUE, -1));
            if(finishAfterResult)
            {
            	finishAfterResult = false;
            	finish();
            }
    }	
	

	// Sets up the framework necessary for gestures to be detected and processed
	@Override
	protected void onCreate(Bundle savedInstanceState)
	{
		super.onCreate(savedInstanceState);
		setContentView(R.layout.page_panel);

		panelRootLayout = (LinearLayout) findViewById(R.id.panel_layout);

		initializeGesturing();

		panelRootLayout.setOnClickListener(new OnClickListener()
		{
			@Override
			public void onClick(View view)
			{
				addRow(); // add row to blank page when pressed
			}
		});

		boolean emptylayoutLoaded = !loadLayout(DEFAULT_LAYOUT_FILE);
		configurationMode = emptylayoutLoaded;
	}
	

	@Override
	public void onCreateContextMenu(ContextMenu menu, View v,
			ContextMenuInfo menuInfo)
	{
		super.onCreateContextMenu(menu, v, menuInfo);
		selectedView = v;
		Log.d(TAG, "Selected view of type: " + v.getClass());
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.context_menu, menu);
	}

	@Override
	public boolean onContextItemSelected(MenuItem item)
	{
		switch (item.getItemId())
		{
		case R.id.addRow:
			addRow();
			return true;
		case R.id.addCol:
			addColumn(selectedView);
			return true;
		case R.id.remove:
			removePanel(selectedView);
			return true;
		case R.id.removeRow:
			removeRow(selectedView);
			return true;
		case R.id.setPanelType:
			((Panel) selectedView).showSetTypeDialog();
			return true;
		default:
			return super.onContextItemSelected(item);
		}
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu)
	{
		MenuInflater inflater = getMenuInflater();
		inflater.inflate(R.menu.options_menu, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item)
	{
		// Handle item selection
		switch (item.getItemId())
		{
		case R.id.toggleConfigurationMode:
			toggleConfigurationMode();
			return true;
		case R.id.removeAll:
			removeAllViews();
			return true;
		case R.id.saveLastGesture:
			gestureHandler.saveLastGesture();
			return true;
		case R.id.saveLayout:
			writeLayout(DEFAULT_LAYOUT_FILE);
			return true;
		default:
			return super.onOptionsItemSelected(item);
		}
	}
	
	

	private void toggleConfigurationMode()
	{
		configurationMode = !configurationMode;
	}

	public boolean inConfigMode()
	{
		return configurationMode;
	}



	// layout save methods
	
	private void writeLayout(String fileName)
	{
		Log.d(TAG, "Writing layout to file " + fileName);
		String layout = layoutToString();

		FileOutputStream fos;
		try
		{
			fos = openFileOutput(fileName, Context.MODE_PRIVATE);
			fos.write(layout.getBytes());
			fos.close();
			Log.d(TAG, "Layout file " + fileName + " written");
			Toast.makeText(this, "Layout saved", Toast.LENGTH_SHORT).show();
		} catch (FileNotFoundException e)
		{
			e.printStackTrace();
		} catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	
	/**
	 * 
	 * @return a String representing the layout which can be used to save the
	 *         state to a file
	 */
	private String layoutToString()
	{
		StringBuilder layout = new StringBuilder();
		for (int i = 0; i < panelRootLayout.getChildCount(); i++) // for each row
		{
			LinearLayout currentRow = (LinearLayout) panelRootLayout
					.getChildAt(i); // we know each child of panelRootLayout is
									// a LinearLayout
			for (int j = 0; j < currentRow.getChildCount(); j++) // for each row
			{
				layout.append(currentRow.getChildAt(j).toString()); // add each Panel's string representation to the layout String
				// if (j != (currentRow.getChildCount() - 1)) // if this isn't
				// the last item in the row
				layout.append(","); // seperate it from next item with a comma
			}
			if (i != (panelRootLayout.getChildCount() - 1)) // if this isn't the
															// last row
				layout.append("-"); // add a new line after each row
		}
		return layout.toString();
	}
	

	private String readLayout(String fileName)
	{
		StringBuilder layout = new StringBuilder();
		try
		{
			FileInputStream fis = openFileInput(fileName);
			DataInputStream in = new DataInputStream(fis);
			BufferedReader br = new BufferedReader(new InputStreamReader(in));
			String strLine;
			// Read File Line By Line
			while ((strLine = br.readLine()) != null)
			{
				layout.append(strLine);
			}
			in.close();

		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
		return layout.toString();
	}
	

	private boolean loadLayout(String filename)
	{
		return setupLayout(readLayout(filename));
	}
	
	
	/**
	 * 
	 * @param layout
	 * @return false if empty layout, true otherwise
	 */
	private boolean setupLayout(String layout)
	{
		if (layout.equals(""))
			return false;					// empty layout
		String[] rows = layout.split("-");
		Log.d(TAG, "rows are: " + Arrays.toString(rows));

		LinearLayout currentRow;
		for (int row = 0; row < rows.length; row++)
		{
			if (rows[row].equals(""))
				continue;
			currentRow = addEmptyRow();
			String[] rowString = rows[row].split(","); // we are parsing a comma-seperated list of row representations
			Log.d(TAG, ("row " + row + " is " + Arrays.toString(rowString)));
			for (int i = 0; i < rowString.length; i++)
			{
				if (rowString[i].length() > 0)
				{
					Log.d(TAG, "Adding Panel for " + rowString[i]);
					addPanel(currentRow, Panel.fromString(this, rowString[i]));
				}
			}
		}
		return true;
	}
}
